//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "flstatus.h"
#include "flreq.h"
#include "flsocket.h"
#include "fatfilt.h"
#include "flioctl.h"
#include "blockdev.h"
#include "docbdk.h"
#include "flformat.h"
#include <config.h>
#include <doc_op.h>

char BDK0_KEY[]="01234567";
char BDK1_KEY[]="12345678";

/* The Multiboot header.  */
typedef struct multiboot_header
{
  unsigned long magic;
  unsigned long flags;
  unsigned long checksum;
  unsigned long header_addr;
  unsigned long load_addr;
  unsigned long load_end_addr;
  unsigned long bss_end_addr;
  unsigned long entry_addr;
} multiboot_header_t;

#define HEADER_OFFSET   12
#define PRELOAD         0x100000

#define DOC_RETURN \
    do {\
        if( status != flOK ) {\
            return -1;\
        }\
        else {\
            return 0;\
        }\
    } while(0)

static IOreq ioreqPar;

IOreq* IoreqPar(FLHandle irHandle, unsigned int irFlags, void *irData, long irLength, long irCount)
{
    ioreqPar.irHandle=(irHandle<<4)+0;
    ioreqPar.irFlags=irFlags;
    ioreqPar.irData=irData;
    ioreqPar.irLength=irLength;
    ioreqPar.irCount=irCount;
    return &ioreqPar;
}

int IdentifyDoc(void)
{
    IOreq   ioreq;
    PhysicalInfo    info;

    FLStatus status;
    status = flInit();
    if (noOfSockets == 0)
        return -1;

    ioreq.irHandle = 0;
    ioreq.irData = &info;
    if (flGetPhysicalInfo (&ioreq) != flOK)
        return -2;

    printk("                      DOC Information\n");
    printk("JEDEC_ID    Type    BlockSize    MediaSize    ChipSize    Interleaving\n");
    printk("%8d    %4d    0x%7x    0x%7x    0x%6x    %d\n", info.type,
           info.mediaType, info.unitSize, info.mediaSize, info.chipSize,
           info.interleaving);

    return 0;
}

int InsertProtectKey(int uPartitionNo, FLByte *pKey)
{
    FLStatus status;

    flProtectionOutput floutput;
    flProtectionInput  flinput;
    flIOctlRecord ioctlrec;

    flinput.type = PROTECTION_INSERT_KEY;
    memcpy(flinput.key, pKey, 8);

    ioctlrec.inputRecord = &flinput;
    ioctlrec.outputRecord = &floutput;

    status = bdIOctl(IoreqPar(uPartitionNo, FL_IOCTL_BINARY_HW_PROTECTION, &ioctlrec, 0, 0));

    if (status != flOK) {
        printk("Insert protecet key to partition %d failed!\n", uPartitionNo);
        return -1;
    }
    else {
        printk("Insert protecet key to partition %d succeed!\n", uPartitionNo);
        return 0;
    }
}

int RemoveProtectKey(int uPartitionNo)
{
    FLStatus status;

    flProtectionOutput floutput;
    flProtectionInput  flinput;
    flIOctlRecord ioctlrec;

    flinput.type = PROTECTION_REMOVE_KEY;

    ioctlrec.inputRecord = &flinput;
    ioctlrec.outputRecord = &floutput;

    status = bdIOctl(IoreqPar(uPartitionNo, FL_IOCTL_BINARY_HW_PROTECTION, &ioctlrec, 0, 0));

    if (status != flOK) {
        printk("Remove protect key from partition %d failed!\n", uPartitionNo);
        return -1;
    }
    else {
        printk("Remove protect key from partition %d succeed!\n", uPartitionNo);
        return 0;
    }
}

int DisableProtectKey()
{
    memset(BDK0_KEY, 0, 8);
    memset(BDK1_KEY, 0, 8);
    return 0;
}

int GetProtectInfo(int uPartitionNo, FLByte *pKey)
{
    FLStatus status;

    flProtectionOutput floutput;
    flProtectionInput flinput;
    flIOctlRecord ioctlrec;

    flinput.type = PROTECTION_GET_TYPE;

    ioctlrec.inputRecord = &flinput;
    ioctlrec.outputRecord = &floutput;

    status = bdIOctl(IoreqPar(uPartitionNo, FL_IOCTL_BINARY_HW_PROTECTION, &ioctlrec, 0, 0));

    if (flOK == status) {
        printk("the protection type of %d is %15s %15s %15s. KEY[%s]\n", uPartitionNo,
                    (floutput.protectionType & 0x02)? "read protect":"",
                    (floutput.protectionType & 0x04)? "write protect":"",
                    (floutput.protectionType & 0x20)? "key is inserted":"",
                    pKey);
        return 0;
    }
    else if (flNotProtected == status) {
        printk("the partition %d is not been protected!\n", uPartitionNo);
        return 0;
    }
    else {
        printk("Get partition %d protect type error!\n", uPartitionNo);
        return 1;
    }
}

int CreateBinarySubpartition(int subPartitionNo, int length, unsigned char sign[4])
{
    BDKStruct BDKStructRec;
    FLStatus  status;
    IOreq SubCreateIOreq;
    PhysicalInfo physicalInfo;

    SubCreateIOreq.irHandle = 0;
    SubCreateIOreq.irData   = &physicalInfo;
    SubCreateIOreq.irLength = 0;
    SubCreateIOreq.irCount  = 0;

    status = flGetPhysicalInfo(&SubCreateIOreq);
    if (status == flOK) {
//        printk("\n\rflGetPhysicalInfo :\n"
//                  "\rFlash device type (JEDEC id)                    %08x\n "
//                  "\rtype of media see below                         %08x\n "
//                  "\rSmallest physically erasable size in sectors    %08x\n "
//                  "\rmedia size in sectors                           %08x\n "
//                  "\rindividual chip size in sectors                 %08x\n "
//                  "\rdevice interleaving                             %08x\n",
//                  physicalInfo.type,
//                  physicalInfo.mediaType,
//                  physicalInfo.unitSize,
//                  physicalInfo.mediaSize,
//                  physicalInfo.chipSize,
//                  physicalInfo.interleaving);
    }
    else {
        printk("flGetPhysicalInfo failed!\n");
        return -1;
    }

    BDKStructRec.startingBlock = 0;
    BDKStructRec.length = (length + physicalInfo.unitSize - 1) / physicalInfo.unitSize;
    BDKStructRec.signOffset=8;

    memcpy (BDKStructRec.oldSign, ((subPartitionNo == 0) ? BDK0_SIGN : BDK1_SIGN), 4);
    memcpy (BDKStructRec.newSign, sign, 4);

    status = bdkCreate(IoreqPar(subPartitionNo,0, &BDKStructRec, 0, 0));

    BDKStructRec.signOffset=8;
    BDKStructRec.startingBlock=0;
    memcpy (BDKStructRec.oldSign, sign, 4);
    status = bdkPartitionInfo (IoreqPar(1,0,&BDKStructRec,0,0) );

    if (status == flOK) {
        printk("Create BDK%d sub-partition %s 0x%x bytes\n", subPartitionNo, sign, BDKStructRec.startingBlock);
        return 0;
    }
    else {
        printk("CreateBinarySubpartition failed.Status = %d\n", status);
        return -1;
    }
}

int GetBinaryPartitionInfo()
{
    BDKStruct BDKStructRec;
    FLStatus  status;

    memcpy(BDKStructRec.oldSign, BDK0_SIGN, 4);
    status = bdkPartitionInfo(IoreqPar(0, 0, &BDKStructRec, 0, 0));
    if (status == flOK) {
        printk("Number of binary partitions = %d\n", BDKStructRec.flags);
        printk("BDK0  : 0x%x bytes\n", BDKStructRec.startingBlock);
    }
    else
        printk("GetBinaryPartitionInfo-----failed!\n");

    memcpy(BDKStructRec.oldSign, BDK1_SIGN, 4);
    status = bdkPartitionInfo(IoreqPar(1, 0, &BDKStructRec, 0, 0));
    if (status == flOK) {
        printk("BDK1  : 0x%x bytes\n", BDKStructRec.startingBlock);
    }
    else
        printk("GetBinaryPartitionInfo-----failed!!\n");

    memcpy(BDKStructRec.oldSign, LOGO_SIGN, 4);
    status = bdkPartitionInfo(IoreqPar(1, 0, &BDKStructRec, 0, 0));
    if (status == flOK) {
        printk("LOGO  : 0x%x bytes\n", BDKStructRec.startingBlock);
    }
    else
        printk("GetBinaryPartitionInfo-----failed!!\n");

    memcpy(BDKStructRec.oldSign, TATT_SIGN, 4);
    status = bdkPartitionInfo(IoreqPar(1, 0, &BDKStructRec, 0, 0));
    if (status == flOK) {
        printk("TATT  : 0x%x bytes\n", BDKStructRec.startingBlock);
    }
    else
        printk("GetBinaryPartitionInfo-----failed!!\n");

    DOC_RETURN;
}

int FormatDoc()
{
    FLStatus status;
    BinaryPartitionFormatParams3 bdkp[] = { STD_BINARY_PARAMS3, STD_BINARY_PARAMS3 };
    BDTLPartitionFormatParams3 bdtlp[] = { STD_BDTL_PARAMS3, STD_BDTL_PARAMS3, STD_BDTL_PARAMS3, STD_BDTL_PARAMS3 };
    FormatParams3 fp = STD_FORMAT_PARAMS3;

    flFlashFormatInput flinput;
    flOutputStatusRecord floutput;
    flIOctlRecord ioctlrec;

    bdkp[0].length = BDK0_LENGTH;
    bdkp[0].flags = TL_SPL_FORMAT;
    bdkp[0].protectionType = WRITE_PROTECTED | PROTECTABLE;
    memcpy(bdkp[0].protectionKey, BDK0_KEY, 8);
    memcpy(bdkp[0].sign, BDK0_SIGN, 4);

    bdkp[1].length = BDK1_LENGTH;
    bdkp[1].flags = TL_NORMAL_FORMAT;
    bdkp[1].protectionType = WRITE_PROTECTED | PROTECTABLE;
    memcpy(bdkp[1].protectionKey, BDK1_KEY, 8);
    memcpy(bdkp[1].sign, BDK1_SIGN, 4);

    bdtlp[0].length = BDTL0_LENGTH;
    bdtlp[0].lengthType = FL_LENGTH_IN_BYTES;

    bdtlp[1].length = BDTL1_LENGTH;
    bdtlp[1].lengthType = FL_LENGTH_IN_BYTES;

#ifdef DOC_SIZE_128M

    bdtlp[2].length = BDTL2_LENGTH;
    bdtlp[2].lengthType = FL_LENGTH_IN_BYTES;

    bdtlp[3].length = BDTL3_LENGTH;
    bdtlp[3].lengthType = FL_LENGTH_IN_BYTES;
#endif

    fp.noOfBDTLPartitions = BDTL_NUMBER;
    fp.noOfBinaryPartitions = BDK_NUMBER;
    fp.BDTLPartitionInfo = bdtlp;
    fp.binaryPartitionInfo = bdkp;

    flinput.dwFormatFlags = TL_NORMAL_FORMAT;
    flinput.sdwNoOfKeeps = 0;
    flinput.fpPtr = &fp;

    ioctlrec.inputRecord = &flinput;
    ioctlrec.outputRecord = &floutput;

    InsertProtectKey(0, BDK0_KEY); //insert the key before format
    InsertProtectKey(1, BDK1_KEY); //insert the key before format

    status = bdIOctl(IoreqPar(0, FL_IOCTL_FLASH_FORMAT, &ioctlrec, 0, 0));
    if (status != flOK) {
        printk("format failed!\n");
        return -1;
    }
    else {
        InsertProtectKey(1, BDK1_KEY);
        CreateBinarySubpartition(1, LOGO_LENGTH, LOGO_SIGN);
        CreateBinarySubpartition(1, TATT_LENGTH, TATT_SIGN);
        RemoveProtectKey(1);  //after the operiton should remove the key immediately
        return 0;
    }
    ;
}

int FormatDocSafe()
{
    int retval;
    int retTAT;

    retval = ReadIPL(RAM_START + IPL_RAM_OFFSET);
    if (retval)
        return -1;

    retval = ReadSPL(RAM_START + SPL_RAM_OFFSET);
    if (retval)
        return -2;

    retTAT = ReadTatTable((unsigned char*)(RAM_START + TATT_RAM_OFFSET));

    retval = FormatDoc();
    if (retval)
        return -3;

    retval = WriteIPL(RAM_START + IPL_RAM_OFFSET);
    if (retval)
        return -4;

    retval = WriteSPL(RAM_START + SPL_RAM_OFFSET);
    if (retval)
        return -5;

    if (!retTAT) {
        retval = WriteTatTable((unsigned char*)(RAM_START + TATT_RAM_OFFSET));
        if (retval)
            return -6;
    }

    return 0;
}

int ReadIPL(int addr)
{
    unsigned char *ptr = (unsigned char*)addr;
    FLStatus status;

    status = flReadIPL(IoreqPar(0,FL_IPL_MODE_NORMAL,ptr,2*1024,0));
    DOC_RETURN;
}

int WriteIPL(int addr)
{
    unsigned char *ptr = (unsigned char*)addr;
    FLStatus status;
    long magic = *((long *)(addr + 0x7f0));
    if (magic != ELDR_MAGIC) {
        printk("Magic of IPL is Error:0x%08x\n", magic);
        return -1;
    }
    InsertProtectKey(0, BDK0_KEY);
    InsertProtectKey(1, BDK1_KEY);
    status = flWriteIPL(IoreqPar(0,FL_IPL_MODE_NORMAL,ptr,2*1024,0));
    RemoveProtectKey(0);
    RemoveProtectKey(1);
    DOC_RETURN;
}

int WriteBDKPartition(int nPartition, const char * sign, int length, int src)
{
    BDKStruct BDKStructRec;
    FLByte *pkey;
    FLStatus status;

    memcpy(BDKStructRec.oldSign, sign, 4);
    BDKStructRec.startingBlock = 0;
    BDKStructRec.signOffset = 8;
    BDKStructRec.length = length;
    BDKStructRec.flags = BDK_COMPLETE_IMAGE_UPDATE;

    if (0 == nPartition) {
        pkey = BDK0_KEY;
    }
    else {
        pkey = BDK1_KEY;
    }
    InsertProtectKey(nPartition, pkey);
    status = bdkWriteInit(IoreqPar(nPartition,0,&BDKStructRec,0,0));
    if (status != flOK) {
        printk("writeInit failed!\n");
        return -2;
    }

    BDKStructRec.startingBlock = 0;
    BDKStructRec.bdkBuffer = (unsigned char*)src;
    BDKStructRec.length = length;

    status = bdkWriteBlock(IoreqPar(nPartition, 0, &BDKStructRec, 0, 0));
    RemoveProtectKey(nPartition);
    DOC_RETURN;
}

int ReadBDKPartition(int nPartition, const char * sign, int length, int dst)
{
    BDKStruct BDKStructRec;
    FLStatus status;

    memcpy(BDKStructRec.oldSign, sign, 4);
    BDKStructRec.startingBlock = 0;
    BDKStructRec.signOffset = 8;
    BDKStructRec.length = length;
    BDKStructRec.flags = BDK_COMPLETE_IMAGE_UPDATE;

    status = bdkReadInit(IoreqPar(nPartition,0,&BDKStructRec,0,0));
    if (status != flOK) {
        return -2;
    }

    BDKStructRec.startingBlock = 0;
    BDKStructRec.bdkBuffer = (unsigned char*)dst;
    BDKStructRec.length = length;

    status = bdkReadBlock(IoreqPar(nPartition, 0, &BDKStructRec, 0, 0));
    DOC_RETURN;
}

int ReadSPL(int addr)
{
    long len = (((*((long *)(0x7fc))) + 0x1ff) & 0xfffffe00) << 1;
    if (len > BDK0_LENGTH || len < 0) {
        printk("Read SPL Error!length:0x%08x\n", len);
        return -1;
    }

    return ReadBDKPartition(0, BDK0_SIGN, len, addr);
}

int WriteSPL(int addr)
{
    long len = (((*((long *)(addr + 0xffc))) + 0x1ff) & 0xfffffe00) << 1;
    long magic = *((long *)(addr + 0xff0));
    if (len > BDK0_LENGTH || len < 0 || magic != ELDR_MAGIC) {
        printk("Write SPL Error!length:0x%08x magic:%08x\n", len, magic);
        return -1;
    }

    return WriteBDKPartition(0, BDK0_SIGN, len, addr);
}

int doc_BDTL_mount(unsigned int partition)
{
    IOreq ioreq;
    FLStatus status;

    ioreq.irHandle=(partition << 4) + 0;
    status=flAbsMountVolume(&ioreq);
    DOC_RETURN;
}

int doc_BDTL_dismount(unsigned int partition)
{
    IOreq ioreq;
    FLStatus status;

    ioreq.irHandle=(partition << 4) + 0;
    status=flDismountVolume(&ioreq);
    DOC_RETURN;
}

int Get_BDTL_Partition_Size(unsigned int ParNo)
{
    IOreq    ioreq;
    FLStatus status;

    ioreq.irHandle = (ParNo << 4) & 0xf0;
    status = flSectorsInVolume(&ioreq);
    if (status == flOK)
        return ioreq.irLength;
    else {
        printk("Get_BDTL_Partiton_Size----error---%d\n", status);
        return 0;
    }
}

int doc_info(void)
{
    int i;
    int size;

    IdentifyDoc();

    //Dump bdk protect info
    GetProtectInfo(0, BDK0_KEY);
    GetProtectInfo(1, BDK1_KEY);

    GetBinaryPartitionInfo();

    for (i = 0; i < BDTL_NUMBER; i++) {
        doc_BDTL_mount(i);
        size = Get_BDTL_Partition_Size(i);
        if (size)
            printk("BDTL%d : 0x%x bytes\n", i, size* 512);
        doc_BDTL_dismount(i);
    }
    return 0;
}

int WriteLogo(int ptr, int size)
{
    return WriteBDKPartition(1, LOGO_SIGN, size, ptr);
}

int ReadLogo(int ptr, int size)
{
    return ReadBDKPartition(1, LOGO_SIGN, size, ptr);
}

int WriteTatTable(unsigned char *buff)
{
    return WriteBDKPartition(1, TATT_SIGN, TATT_LENGTH, (int)buff);
}

int ReadTatTable(unsigned char *buff)
{
    int ret;

    ret = ReadBDKPartition(1, TATT_SIGN, TATT_LENGTH, (int)buff);
    printk ("ReadTAT, return %d\n", ret);

    //if (!ret)
    {
        int i, j;
        for (i = 0; i < 400; i++) {
            for (j = 0; j < 16; j++) {
                printk ("%02x ", *(buff + i * 16 + j));
            }
            if ((i + 1) % 16 == 0)
                printk ("\n                                                              \n");
            printk ("\n");
        }
    }

    return 0;//ret
}

int ReadImage(int to)
{
    BDKStruct BDKStructRec;
    FLStatus status;
    multiboot_header_t *mbheader;
    unsigned long  nImgSize = 0;

    memcpy(BDKStructRec.oldSign, BDK1_SIGN, 4);
    BDKStructRec.signOffset = 8;
    BDKStructRec.startingBlock = 0;
    BDKStructRec.length = KERNEL_LENGTH;
    BDKStructRec.flags = BDK_COMPLETE_IMAGE_UPDATE;
    status = bdkReadInit(IoreqPar(1, 0, &BDKStructRec, 0, 0));
    if (status != flOK) {
        return -2;
    }

    BDKStructRec.startingBlock = 0;
    BDKStructRec.bdkBuffer = (unsigned char*)to;
    BDKStructRec.length = PRELOAD;
    status = bdkReadBlock(IoreqPar(1, 0, &BDKStructRec, 0, 0));
    if (status != flOK) {
        return -3;
    }

    mbheader = (multiboot_header_t*)(to + HEADER_OFFSET);
//    printk("multiboot header info:\n");
//    printk("load_addr    = 0x%lx\n", mbheader->load_addr);
//    printk("load_end_addr= 0x%lx\n", mbheader->load_end_addr);
//    printk("bss_end_addr = 0x%lx\n", mbheader->bss_end_addr);
//    printk("entry_addr   = 0x%lx\n", mbheader->entry_addr);
    nImgSize = mbheader->load_end_addr - mbheader->load_addr;
//    printk("Elastos image size %d\n", nImgSize);

    if (nImgSize <= PRELOAD)
        return 0;
    else if (nImgSize > KERNEL_LENGTH)
        return -4;

    BDKStructRec.bdkBuffer = (unsigned char*)(to + PRELOAD);
    BDKStructRec.length = (nImgSize - PRELOAD + 0x1ff) & ~0x1ff;
//    printk("add %d\n", BDKStructRec.length);
    status = bdkReadBlock(IoreqPar(1, 0, &BDKStructRec, 0, 0));
    DOC_RETURN;
}

int WriteImage(int from)
{
    multiboot_header_t *mbheader;
    unsigned long  nImgSize = 0;

    mbheader = (multiboot_header_t*)(from + HEADER_OFFSET);
    nImgSize = mbheader->load_end_addr - mbheader->load_addr;

    if (nImgSize <= 0 || nImgSize > KERNEL_LENGTH)
        return -2;

    return WriteBDKPartition(1, BDK1_SIGN, nImgSize, from);
}

int WriteBoot(int addr, int len)
{
    unsigned char *ptr = (unsigned char*)addr;
    FLStatus status;
    InsertProtectKey(0, BDK0_KEY);
    InsertProtectKey(1, BDK1_KEY);
    status = flWriteIPL(IoreqPar(0,FL_IPL_MODE_NORMAL,ptr,2*1024,0));
    RemoveProtectKey(0);
    RemoveProtectKey(1);
    if( status != flOK ) {
        printk("Write Ipl failed\n");
        return -1;
    }
    return WriteBDKPartition(0, BDK0_SIGN, len, addr);
}

int ReadBoot(int addr, int len)
{
    return ReadBDKPartition(0, BDK0_SIGN, len, addr);
}
